package main

import (
	"context"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
)

type Student struct {
	Name string
	Age  int
}

func main() {
	// 设置客户端连接配置
	clientOptions := options.Client().ApplyURI("mongodb://127.0.0.12.9:27017")

	// 连接到MongoDB
	client, err := mongo.Connect(context.TODO(), clientOptions)
	if err != nil {
		log.Fatal(err)
	}

	// 检查连接
	err = client.Ping(context.TODO(), nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to MongoDB\n")

	// 获取集合
	collection := client.Database("test").Collection("trainers")

	// 插入文档
	s1 := Student{"小明", 12}
	s2 := Student{"小红", 13}
	s3 := Student{"小王", 14}
	// 使用collection.InsertOne() 方法插入一条文档记录
	insertResult, err := collection.InsertOne(context.TODO(), s1)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("\n Inserted a single document: ", insertResult.InsertedID)
	// 使用collection.InsertMany() 方法插入多条文档记录;
	students := []interface{}{s2, s3}
	insertManyResult, err := collection.InsertMany(context.TODO(), students)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Inserted multiple documents: ", insertManyResult.InsertedIDs)

	// 更新文档
	// 需要一个筛选器文档来匹配数据库中的文档，并需要一个更新文档来描述更新操作。你可以使用bson.D类型来构建筛选文档和更新文档
	filter := bson.D{{"name", "小王"}}
	update := bson.D{
		{"$inc", bson.D{
			{"age", 1},
		}},
	}
	// updateone()方法允许你更新单个文档 通过下面的语句找到小兰，给他增加一岁
	updateResult, err := collection.UpdateOne(context.TODO(), filter, update)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("\n Matched %v documents and updated %v documents.\n", updateResult.MatchedCount, updateResult.ModifiedCount)

	// 查找文档
	// 创建一个Student变量用来接收查询的结果
	var result Student
	// 这个方法返回一个可以解码为值的结果
	err = collection.FindOne(context.TODO(), filter).Decode(&result)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("\nFound a single document: %+v\n", result)

	// 查询多个
	// 将选项传递给Find()
	findOptions := options.Find()
	findOptions.SetLimit(3) // 用options包设置一个限制以便只返回2个文档
	findOptions.SetSkip(1)

	// 定义一个切片用来存储查询结果
	var results []*Student

	// 把bson.D{{}} 作为一个filter来匹配所有文档
	// 此方法返回一个游标。游标提供了一个文档流，你可以通过它一次迭代和解码一个文档。当游标用完之后，应该关闭游标。
	cur, err := collection.Find(context.TODO(), bson.D{{}}, findOptions)
	if err != nil {
		log.Fatal(err)
	}

	// 查找多个文档返回一个光标
	// 遍历游标允许我一次解码一个文档
	for cur.Next(context.TODO()) {
		// 创建一个值，将单个文档解码为该值
		var elem Student
		err := cur.Decode(&elem)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("Found multiple document : %+v\n", elem)
		results = append(results, &elem)
	}

	if err := cur.Err(); err != nil {
		log.Fatal(err)
	}

	// 完成后关闭游标
	cur.Close(context.TODO())
	fmt.Printf("\n Found multiple documents (array of pointers): %+v\n\n", results)

	// 删除文档
	// 使用collection.DeleteOne()或collection.DeleteMany()删除文档。
	// 如果你传递bson.D{{}}作为过滤器参数，它将匹配数据集中的所有文档。还可以使用collection. drop()删除整个数据集。
	// 删除名字是小黄的那个
	deleteResult, err := collection.DeleteOne(context.TODO(), bson.D{{"name", "小邓"}})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("\n Deleted %v documents in the trainers collection \n", deleteResult.DeletedCount)
	// 删除所有
	deleteResult2, err := collection.DeleteMany(context.TODO(), bson.D{{}})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("\n Deleted %v documents in the trainers collectin \n", deleteResult2.DeletedCount)

	err = collection.Drop(context.TODO())
	if err != nil {
		log.Fatal(err)
	} else {
		fmt.Println("\n Drop document")
	}

	// Close the connection once on longer needed
	err = client.Disconnect(context.TODO())

	if err != nil {
		log.Fatal(err)
	} else {
		fmt.Println("\n Connection to MongoDB closed.")
	}

}
